/**
 * Mock API service to simulate backend responses
 * This file provides mock API responses for development and testing
 */

import { mockModel, mockBusinessObjects } from './data/mockModel';

/**
 * A class to provide mock API responses based on the mock data
 */
export class MockApiService {
  private static instance: MockApiService;
  private mockData: Map<string, any[]> = new Map();
  private idCounters: Map<string, number> = new Map();
  
  private constructor() {
    this.initializeData();
  }
  
  /**
   * Get the singleton instance of the MockApiService
   */
  public static getInstance(): MockApiService {
    if (!MockApiService.instance) {
      MockApiService.instance = new MockApiService();
    }
    return MockApiService.instance;
  }
  
  /**
   * Initialize the mock data store with sample data
   */
  private initializeData(): void {
    // Initialize data for each business object
    mockBusinessObjects.forEach(bo => {
      const typeName = bo.name;
      this.mockData.set(typeName, this.generateMockData(typeName, 20));
      this.idCounters.set(typeName, 21); // Start with ID after the generated data
    });
    
    console.log('Mock data initialized');
  }
  
  /**
   * Generate mock data for a specific business object type
   * @param typeName Business object type name
   * @param count Number of records to generate
   */
  private generateMockData(typeName: string, count: number): any[] {
    const result: any[] = [];
    
    // Generate data based on business object type
    switch (typeName) {
      case 'Customer':
        for (let i = 1; i <= count; i++) {
          result.push({
            id: i,
            name: `客户 ${i}`,
            phone: `1390000${String(i).padStart(4, '0')}`,
            email: `customer${i}@example.com`,
            address: `北京市朝阳区示例路 ${i} 号`,
            createdAt: new Date().toISOString()
          });
        }
        break;
        
      case 'Product':
        for (let i = 1; i <= count; i++) {
          result.push({
            id: i,
            name: `产品 ${i}`,
            code: `P${String(i).padStart(5, '0')}`,
            price: Math.round(Math.random() * 1000 * 100) / 100,
            description: `这是产品 ${i} 的详细描述信息，描述了产品的特点和使用方法。`,
            categoryId: Math.floor(Math.random() * 5) + 1, // Random category 1-5
            isActive: Math.random() > 0.2 // 80% chance of being active
          });
        }
        break;
        
      case 'Category':
        for (let i = 1; i <= 5; i++) {
          result.push({
            id: i,
            name: `类别 ${i}`,
            description: `类别 ${i} 的详细描述`,
            parentId: i > 1 ? (Math.random() > 0.7 ? Math.floor(Math.random() * (i - 1)) + 1 : null) : null
          });
        }
        break;
        
      case 'Order':
        for (let i = 1; i <= count; i++) {
          result.push({
            id: i,
            orderNumber: `ORD${String(i).padStart(6, '0')}`,
            customerId: Math.floor(Math.random() * count) + 1,
            orderDate: new Date(Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000).toISOString(), // Random date in last 30 days
            status: ['new', 'processing', 'shipped', 'delivered', 'cancelled'][Math.floor(Math.random() * 5)],
            totalAmount: Math.round(Math.random() * 10000 * 100) / 100
          });
        }
        break;
        
      case 'OrderItem':
        const orderIds = Array.from({ length: count }, (_, i) => i + 1);
        let itemId = 1;
        
        // Create 1-5 items for each order
        for (const orderId of orderIds) {
          const itemCount = Math.floor(Math.random() * 5) + 1;
          
          for (let i = 0; i < itemCount; i++) {
            const productId = Math.floor(Math.random() * count) + 1;
            const quantity = Math.floor(Math.random() * 5) + 1;
            const unitPrice = Math.round(Math.random() * 500 * 100) / 100;
            
            result.push({
              id: itemId++,
              orderId,
              productId,
              quantity,
              unitPrice,
              amount: quantity * unitPrice,
              discount: Math.floor(Math.random() * 20)
            });
          }
        }
        break;
    }
    
    return result;
  }
  
  /**
   * Get the application model
   */
  public getApplicationModel(): any {
    return { ...mockModel };
  }
  
  /**
   * Get a list of objects of a specific type, with OData-like querying
   * @param typeName The business object type name
   * @param filter Filter expression (simplified)
   * @param sort Sort information
   * @param skip Number of records to skip
   * @param take Maximum number of records to return
   * @param expand Properties to expand
   * @param select Properties to select
   */
  public getObjects(
    typeName: string, 
    filter?: string,
    sort?: { field: string, dir: 'asc' | 'desc' }[],
    skip?: number,
    take?: number,
    expand?: string[],
    select?: string[]
  ): { value: any[], "@odata.count": number } {
    const data = this.mockData.get(typeName) || [];
    let result = [...data];
    
    // Apply filter (very simplified implementation)
    if (filter) {
      // Simple filtering for demo purposes - supports only basic equality filters
      const filterParts = filter.split(' ');
      if (filterParts.length === 3 && filterParts[1] === 'eq') {
        const field = filterParts[0];
        let value = filterParts[2];
        
        // Remove quotes for string values
        if (value.startsWith("'") && value.endsWith("'")) {
          value = value.substring(1, value.length - 1);
        } else if (value === 'true' || value === 'false') {
          value = value === 'true' ? 'true' : 'false';
        } else if (!isNaN(Number(value))) {
          // value remains a string, but we will compare as number below
        }
        
        result = result.filter(item => {
          if (typeof item[field] === 'boolean') {
            return String(item[field]) === value;
          }
          if (!isNaN(Number(value)) && typeof item[field] === 'number') {
            return item[field] === Number(value);
          }
          return item[field] === value;
        });
      }
    }
    
    // Apply sorting
    if (sort && sort.length > 0) {
      result.sort((a, b) => {
        for (const s of sort) {
          const direction = s.dir === 'asc' ? 1 : -1;
          if (a[s.field] < b[s.field]) return -1 * direction;
          if (a[s.field] > b[s.field]) return 1 * direction;
        }
        return 0;
      });
    }
    
    // Count total before paging
    const totalCount = result.length;
    
    // Apply paging
    if (skip !== undefined) {
      result = result.slice(skip);
    }
    if (take !== undefined) {
      result = result.slice(0, take);
    }
    
    // Handle expansion - this is a simple implementation that assumes
    // we have the associations available in our mock data store
    if (expand && expand.length > 0) {
      result = result.map(item => {
        const expanded = { ...item };
        
        for (const expandProp of expand) {
          const bo = mockBusinessObjects.find(bo => bo.name === typeName);
          if (bo && bo.associations) {
            const association = bo.associations.find(a => a.name === expandProp);
            if (association) {
              const targetData = this.mockData.get(association.targetType) || [];
              
              if (association.associationType === 'collection') {
                expanded[expandProp] = targetData.filter(
                  targetItem => targetItem[association.keyProperty] === item.id
                );
              } else {
                expanded[expandProp] = targetData.find(
                  targetItem => targetItem.id === item[association.keyProperty]
                );
              }
            }
          }
        }
        
        return expanded;
      });
    }
    
    // Handle select - limit the fields returned
    if (select && select.length > 0) {
      result = result.map(item => {
        const selectedItem: any = {};
        for (const field of select) {
          selectedItem[field] = item[field];
        }
        return selectedItem;
      });
    }
    
    return {
      value: result,
      "@odata.count": totalCount
    };
  }
  
  /**
   * Get a single object by key
   * @param typeName Business object type name
   * @param key Key value
   * @param expand Properties to expand
   * @param select Properties to select
   */
  public getObjectByKey(
    typeName: string,
    key: any,
    expand?: string[],
    select?: string[]
  ): any {
    const data = this.mockData.get(typeName) || [];
    let item = data.find(item => item.id === key);
    
    if (!item) {
      return null;
    }
    
    // Handle expansion
    if (expand && expand.length > 0) {
      const expanded = { ...item };
      
      for (const expandProp of expand) {
        const bo = mockBusinessObjects.find(bo => bo.name === typeName);
        if (bo && bo.associations) {
          const association = bo.associations.find(a => a.name === expandProp);
          if (association) {
            const targetData = this.mockData.get(association.targetType) || [];
            
            if (association.associationType === 'collection') {
              expanded[expandProp] = targetData.filter(
                targetItem => targetItem[association.keyProperty] === item.id
              );
            } else {
              expanded[expandProp] = targetData.find(
                targetItem => targetItem.id === item[association.keyProperty]
              );
            }
          }
        }
      }
      
      item = expanded;
    }
    
    // Handle select
    if (select && select.length > 0) {
      const selectedItem: any = {};
      for (const field of select) {
        selectedItem[field] = item[field];
      }
      return selectedItem;
    }
    
    return item;
  }
  
  /**
   * Create a new object
   * @param typeName Business object type name
   * @param data Object data
   */
  public createObject(typeName: string, data: any): any {
    const objects = this.mockData.get(typeName) || [];
    const idCounter = this.idCounters.get(typeName) || 1;
    
    // Create new object with generated ID
    const newObject = {
      ...data,
      id: idCounter
    };
    
    // Add to mock data store
    objects.push(newObject);
    this.mockData.set(typeName, objects);
    this.idCounters.set(typeName, idCounter + 1);
    
    return newObject;
  }
  
  /**
   * Update an existing object
   * @param typeName Business object type name
   * @param key Key value
   * @param data Updated data
   */
  public updateObject(typeName: string, key: any, data: any): any {
    const objects = this.mockData.get(typeName) || [];
    const index = objects.findIndex(item => item.id === key);
    
    if (index >= 0) {
      // Update object
      objects[index] = { ...objects[index], ...data };
      this.mockData.set(typeName, objects);
      return objects[index];
    }
    
    throw new Error(`Object of type ${typeName} with key ${key} not found`);
  }
  
  /**
   * Delete an object
   * @param typeName Business object type name
   * @param key Key value
   */
  public deleteObject(typeName: string, key: any): void {
    const objects = this.mockData.get(typeName) || [];
    const index = objects.findIndex(item => item.id === key);
    
    if (index >= 0) {
      // Remove object
      objects.splice(index, 1);
      this.mockData.set(typeName, objects);
      return;
    }
    
    throw new Error(`Object of type ${typeName} with key ${key} not found`);
  }
}
